package com.starsea.book.service

import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.lifecycle.lifecycleScope
import com.blankj.utilcode.util.LogUtils
import com.starsea.book.R
import com.starsea.book.base.BaseService
import com.starsea.book.constant.AppConst
import com.starsea.book.constant.EventBus
import com.starsea.book.constant.IntentAction
import com.starsea.book.constant.NotificationId
import com.starsea.book.data.appDb
import com.starsea.book.help.config.AppConfig
import com.starsea.book.model.CacheBook
import com.starsea.book.ui.book.cache.CacheActivity
import com.starsea.book.utils.activityPendingIntent
import com.starsea.book.utils.postEvent
import com.starsea.book.utils.servicePendingIntent
import kotlinx.coroutines.Job
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import splitties.init.appCtx
import java.util.concurrent.Executors
import kotlin.math.min

/**
 * 缓存书籍服务
 */
class CacheBookService : BaseService() {

    companion object {
        var isRun = false
            private set
    }

    private val threadCount = AppConfig.threadCount
    private var cachePool =
        Executors.newFixedThreadPool(min(threadCount, AppConst.MAX_THREAD)).asCoroutineDispatcher()
    private var downloadJob: Job? = null
    private var notificationContent = appCtx.getString(R.string.service_starting)
    private val notificationBuilder by lazy {
        val builder = NotificationCompat.Builder(this, AppConst.channelIdDownload)
            .setSmallIcon(R.drawable.ic_download)
            .setOngoing(true)
            .setContentTitle(getString(R.string.offline_cache))
            .setContentIntent(activityPendingIntent<CacheActivity>("cacheActivity"))
        builder.addAction(
            R.drawable.ic_stop_black_24dp,
            getString(R.string.cancel),
            servicePendingIntent<CacheBookService>(IntentAction.stop)
        )
        builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
    }

    override fun onCreate() {
        super.onCreate()
        isRun = true
        CacheBook.successDownloadSet.clear()
        CacheBook.errorDownloadMap.clear()
        lifecycleScope.launch {
            while (isActive) {
                delay(1000)
                notificationContent = CacheBook.downloadSummary
                upNotification()
                postEvent(EventBus.UP_DOWNLOAD, "")
            }
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        intent?.action?.let { action ->
            when (action) {
                IntentAction.start -> addDownloadData(
                    intent.getStringExtra("bookUrl"),
                    intent.getIntExtra("start", 0),
                    intent.getIntExtra("end", 0)
                )

                IntentAction.remove -> removeDownload(intent.getStringExtra("bookUrl"))
                IntentAction.stop -> stopSelf()
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        isRun = false
        cachePool.close()
        CacheBook.cacheBookMap.forEach { it.value.stop() }
        CacheBook.cacheBookMap.clear()
        CacheBook.successDownloadSet.clear()
        CacheBook.errorDownloadMap.clear()
        super.onDestroy()
        postEvent(EventBus.UP_DOWNLOAD, "")
    }

    private fun addDownloadData(bookUrl: String?, start: Int, end: Int) {
        bookUrl ?: return

        LogUtils.e(">>>>> download", "bookUrl = " + bookUrl)

        execute {
            LogUtils.e(">>>>> download", "bookUrl = " + bookUrl)
            val cacheBook = CacheBook.getOrCreate(bookUrl) ?: return@execute
            LogUtils.e(">>>>> download", "cacheBook = " + cacheBook.book)
            val chapterCount = appDb.bookChapterDao.getChapterCount(bookUrl)

            LogUtils.e(">>>> download", "chapterCount = " + chapterCount)
//            if (chapterCount == 0) {
//                val name = cacheBook.book.name
//                WebBook.getChapterListAwait(cacheBook.bookSource, cacheBook.book).onFailure {
//                    cacheBook.book.totalChapterNum = 0
//                    AppLog.put("《$name》目录为空且加载目录失败\n${it.localizedMessage}", it)
//                    appCtx.toastOnUi("《$name》目录为空且加载目录失败\n${it.localizedMessage}")
//                }.getOrNull()?.let { toc ->
//                    appDb.bookChapterDao.insert(*toc.toTypedArray())
//                }
//                cacheBook.book.save()
//            }
            val end2 = if (end == 0) {
                appDb.bookChapterDao.getChapterCount(bookUrl)
            } else {
                end
            }
            cacheBook.addDownload(start, end2)
            notificationContent = CacheBook.downloadSummary
            upNotification()
            if (downloadJob == null) {
                download()
            }
        }
    }

    private fun removeDownload(bookUrl: String?) {
        CacheBook.cacheBookMap[bookUrl]?.stop()
        postEvent(EventBus.UP_DOWNLOAD, "")
        if (downloadJob == null && CacheBook.isRun) {
            download()
            return
        }
        if (CacheBook.cacheBookMap.isEmpty()) {
            stopSelf()
        }
    }

    private fun download() {
        downloadJob?.cancel()
        downloadJob = lifecycleScope.launch(cachePool) {
            while (isActive) {
                if (!CacheBook.isRun) {
                    CacheBook.stop(this@CacheBookService)
                    return@launch
                }
                CacheBook.cacheBookMap.forEach {
                    val cacheBookModel = it.value
                    while (cacheBookModel.waitCount > 0) {
                        if (CacheBook.onDownloadCount < threadCount) {
                            cacheBookModel.download(this, cachePool)
                        } else {
                            delay(100)
                        }
                    }
                }
            }
        }
    }

    /**
     * 更新通知
     */
    override fun upNotification() {
        notificationBuilder.setContentText(notificationContent)
        val notification = notificationBuilder.build()
        startForeground(NotificationId.CacheBookService, notification)
    }

}